{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using RetrieveChat Powered by Couchbase Capella for Retrieve Augmented Code Generation and Question Answering\n",
    "\n",
    "AG2 offers conversable agents powered by LLM, tool or human, which can be used to perform tasks collectively via automated chat. This framework allows tool use and human participation through multi-agent conversation.\n",
    "Please find documentation about this feature [here](https://microsoft.github.io/autogen/docs/Use-Cases/agent_chat).\n",
    "\n",
    "RetrieveChat is a conversational system for retrieval-augmented code generation and question answering. In this notebook, we demonstrate how to utilize RetrieveChat to generate code and answer questions based on customized documentations that are not present in the LLM's training dataset. RetrieveChat uses the `AssistantAgent` and `RetrieveUserProxyAgent`, which is similar to the usage of `AssistantAgent` and `UserProxyAgent` in other notebooks (e.g., [Automated Task Solving with Code Generation, Execution & Debugging](https://github.com/ag2ai/ag2/blob/main/notebook/agentchat_auto_feedback_from_code_execution.ipynb)). Essentially, `RetrieveUserProxyAgent` implement a different auto-reply mechanism corresponding to the RetrieveChat prompts.\n",
    "\n",
    "## Table of Contents\n",
    "We'll demonstrate six examples of using RetrieveChat for code generation and question answering:\n",
    "\n",
    "- [Example 1: Generate code based off docstrings w/o human feedback](#example-1)\n",
    "\n",
    "````{=mdx}\n",
    ":::info Requirements\n",
    "Some extra dependencies are needed for this notebook, which can be installed via pip:\n",
    "\n",
    "```bash\n",
    "pip install ag2[openai,retrievechat-couchbase] flaml[automl]\n",
    "```\n",
    "\n",
    "For more information, please refer to the [installation guide](https://docs.ag2.ai/latest/docs/quick-start).\n",
    ":::\n",
    "````\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup Couchbase Capella Cluster\n",
    "\n",
    "Before we proceed with the notebook. We will require a Couchbase Capella Database Cluster Running. \n",
    "\n",
    "- To setup a free operational cluster, Head over [Couchbase Cloud](https://cloud.couchbase.com) and create an account. Over there create a free cluster. For more details on creating a cluster, [refer here](https://docs.couchbase.com/cloud/get-started/create-account.html).\n",
    "\n",
    "- After creating the cluster, We will create our required bucket, scope, collections. Head over to <b>Data Tools</b>. On the left hand side panel, you will get an option to create a bucket. Give appropriate Bucket Name, Scope Name, Collection Name. For this tutorial, set\n",
    "    - Bucket Name : test_db\n",
    "    - Scope Name: test_scope\n",
    "    - Collection Name: demo_collection\n",
    "\n",
    "- Now, we will connect to the cluster. [Refer this page for connection](https://docs.couchbase.com/cloud/get-started/connect.html)\n",
    "\n",
    "- Next, let's create user to connect. Head over to settings tab. Click [Create Cluster Access]. Specify a username and password and give read/write all buckets access. You may create more users with less than admin access. For more information, [refer here](https://docs.couchbase.com/cloud/clusters/manage-database-users.html#create-database-credentials)\n",
    "\n",
    "- Add IP Address to allowed IPs. in settings, click on [Networking]. Add [allowed IP](https://docs.couchbase.com/cloud/clusters/allow-ip-address.html) based on your requirements. \n",
    "\n",
    "- Let's fill our environment variables. First get the connection string from the [Connect] Tab. Now we will have 3 envs for Couchbase cluster and one for open AI\n",
    "    - CB_CONN_STR : Couchbase Cluster Connection string\n",
    "    - CB_USERNAME : Username of the user, created above\n",
    "    - CB_PASSWORD : Password of the user, created above\n",
    "    - OPENAI_API_KEY : OpenAI API Key which agents will require.\n",
    "\n",
    "Voila, your cluster is ready to be used."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set your API Endpoint\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "\n",
    "from autogen import AssistantAgent\n",
    "\n",
    "sys.path.append(os.path.abspath(\"/workspaces/autogen/autogen/agentchat/contrib\"))\n",
    "\n",
    "from autogen.agentchat.contrib.retrieve_user_proxy_agent import RetrieveUserProxyAgent\n",
    "\n",
    "# Accepted file formats for that can be stored in\n",
    "# a vector database instance\n",
    "from autogen.retrieve_utils import TEXT_FORMATS\n",
    "\n",
    "config_list = [{\"model\": \"gpt-5-nano\", \"api_key\": os.environ[\"OPENAI_API_KEY\"], \"api_type\": \"openai\"}]\n",
    "assert len(config_list) > 0\n",
    "print(\"models to use: \", [config_list[i][\"model\"] for i in range(len(config_list))])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "````{=mdx}\n",
    ":::tip\n",
    "Learn more about configuring LLMs for agents [here](https://docs.ag2.ai/latest/docs/user-guide/basic-concepts/llm-configuration).\n",
    ":::\n",
    "````\n",
    "\n",
    "## Construct agents for RetrieveChat\n",
    "\n",
    "We start by initializing the `AssistantAgent` and `RetrieveUserProxyAgent`. The system message needs to be set to \"You are a helpful assistant.\" for AssistantAgent. The detailed instructions are given in the user message. Later we will use the `RetrieveUserProxyAgent.message_generator` to combine the instructions and a retrieval augmented generation task for an initial prompt to be sent to the LLM assistant."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Accepted file formats for `docs_path`:\")\n",
    "print(TEXT_FORMATS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. create an AssistantAgent instance named \"assistant\"\n",
    "assistant = AssistantAgent(\n",
    "    name=\"assistant\",\n",
    "    system_message=\"You are a helpful assistant.\",\n",
    "    llm_config={\n",
    "        \"timeout\": 600,\n",
    "        \"cache_seed\": 42,\n",
    "        \"config_list\": config_list,\n",
    "    },\n",
    ")\n",
    "\n",
    "# 2. create the RetrieveUserProxyAgent instance named \"ragproxyagent\"\n",
    "# Refer to https://docs.ag2.ai/latest/docs/api-reference/autogen/agentchat/contrib/retrieve_user_proxy_agent/RetrieveUserProxyAgent/#autogen.agentchat.contrib.retrieve_user_proxy_agent.RetrieveUserProxyAgent\n",
    "# and https://docs.ag2.ai/docs/reference/agentchat/contrib/vectordb/couchbase\n",
    "# for more information on the RetrieveUserProxyAgent and CouchbaseVectorDB\n",
    "ragproxyagent = RetrieveUserProxyAgent(\n",
    "    name=\"ragproxyagent\",\n",
    "    human_input_mode=\"NEVER\",\n",
    "    max_consecutive_auto_reply=3,\n",
    "    retrieve_config={\n",
    "        \"task\": \"code\",\n",
    "        \"docs_path\": [\n",
    "            \"https://raw.githubusercontent.com/microsoft/FLAML/main/website/docs/Examples/Integrate%20-%20Spark.md\",\n",
    "            \"https://raw.githubusercontent.com/microsoft/FLAML/main/website/docs/Research.md\",\n",
    "        ],\n",
    "        \"chunk_token_size\": 2000,\n",
    "        \"model\": config_list[0][\"model\"],\n",
    "        \"vector_db\": \"couchbase\",  # Couchbase Capella VectorDB\n",
    "        \"collection_name\": \"demo_collection\",  # Couchbase Capella collection name to be utilized/created\n",
    "        \"db_config\": {\n",
    "            \"connection_string\": os.environ[\"CB_CONN_STR\"],  # Couchbase Capella connection string\n",
    "            \"username\": os.environ[\"CB_USERNAME\"],  # Couchbase Capella username\n",
    "            \"password\": os.environ[\"CB_PASSWORD\"],  # Couchbase Capella password\n",
    "            \"bucket_name\": \"test_db\",  # Couchbase Capella bucket name\n",
    "            \"scope_name\": \"test_scope\",  # Couchbase Capella scope name\n",
    "            \"index_name\": \"vector_index\",  # Couchbase Capella index name to be created\n",
    "        },\n",
    "        \"get_or_create\": True,  # set to False if you don't want to reuse an existing collection\n",
    "        \"overwrite\": False,  # set to True if you want to overwrite an existing collection, each overwrite will force a index creation and reupload of documents\n",
    "    },\n",
    "    code_execution_config=False,  # set to False if you don't want to execute the code\n",
    ")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example 1\n",
    "\n",
    "[Back to top](#table-of-contents)\n",
    "\n",
    "Use RetrieveChat to help generate sample code and automatically run the code and fix errors if there is any.\n",
    "\n",
    "Problem: Which API should I use if I want to use FLAML for a classification task and I want to train the model in 30 seconds. Use spark to parallel the training. Force cancel jobs if time limit is reached.\n",
    "\n",
    "Note: You may need to create an index on the cluster to query"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# reset the assistant. Always reset the assistant before starting a new conversation.\n",
    "assistant.reset()\n",
    "\n",
    "# given a problem, we use the ragproxyagent to generate a prompt to be sent to the assistant as the initial message.\n",
    "# the assistant receives the message and generates a response. The response will be sent back to the ragproxyagent for processing.\n",
    "# The conversation continues until the termination condition is met, in RetrieveChat, the termination condition when no human-in-loop is no code block detected.\n",
    "# With human-in-loop, the conversation will continue until the user says \"exit\".\n",
    "code_problem = \"How can I use FLAML to perform a classification task and use spark to do parallel training. Train 30 seconds and force cancel jobs if time limit is reached.\"\n",
    "chat_result = ragproxyagent.initiate_chat(assistant, message=ragproxyagent.message_generator, problem=code_problem)"
   ]
  }
 ],
 "metadata": {
  "front_matter": {
   "description": "Explore the use of AG2's RetrieveChat for tasks like code generation from docstrings, answering complex questions with human feedback, and exploiting features like Update Context, custom prompts, and few-shot learning.",
   "tags": [
    "RAG"
   ]
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  },
  "skip_test": "Requires interactive usage"
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
